using BepuPhysics.Collidables;
using BepuUtilities.Memory;
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace BepuPhysics
{
    // TODO：如果有一个与索引对齐的版本就好了。实现起来相当复杂,因为索引移动很多,并且涉及复杂的多线程记账。
    /// <summary>
    /// 方便的集合,存储有关身体和静力学的额外属性,按身体或静电句柄索引。
    /// </summary>
    /// <typeparam name="T">要存储的数据的类型。</typeparam>
    /// <remarks>This is built for use cases relying on random access like the narrow phase. For maximum performance with sequential access, an index-aligned structure would be better.</remarks>
    public class CollidableProperty<T> : IDisposable where T : unmanaged
    {
        // 形体和静力学都有"句柄空间",就像名称空间一样。Body和静电可以具有相同的整数值句柄。
        // 因此,我们需要有两个不同的数据缓冲区。
        Simulation simulation;
        BufferPool pool;
        Buffer<T> bodyData;
        Buffer<T> staticData;

        // Split构造函数/Initialize看起来有点奇怪,但这经常用于窄阶段回调。在模拟之前,实例需要存在。

        /// <summary>
        /// 构造一个新集合来存储句柄对齐的正文属性。假定稍后将调用Initialize函数来提供Body集合。
        /// </summary>
        /// 要从中提取内部资源的<param name="pool">池。如果为NULL,则使用稍后初始化提供的正文池。</param>
        public CollidableProperty(BufferPool pool = null)
        {
            this.pool = pool;
        }

        /// <summary>
        /// 构造一个新集合来存储句柄对齐的正文和静电属性。
        /// </summary>
        /// <param name="Simulation">要跟踪的模拟。</param>
        /// 要从中提取内部资源的<param name="pool">池。如果为NULL,则使用模拟池。</param>
        public CollidableProperty(Simulation simulation, BufferPool pool = null)
        {
            this.simulation = simulation;
            this.pool = pool == null ? simulation.BufferPool : pool;
            this.pool.TakeAtLeast(simulation.Bodies.HandleToLocation.Length, out bodyData);
            this.pool.TakeAtLeast(simulation.Statics.HandleToIndex.Length, out staticData);
        }

        /// <summary>
        /// 如果使用Body/Less-Statics构造函数,则初始化属性集合。
        /// </summary>
        /// <param name="Body">要跟踪的Body集合。</param>
        /// <param name="Statics">要跟踪的静态集合。</param>
        public void Initialize(Simulation simulation)
        {
            if (this.simulation != null)
                throw new InvalidOperationException("Initialize should only be used on a collection which was constructed without defining the Simulation.");
            this.simulation = simulation;
            if (pool == null)
                pool = simulation.Bodies.Pool;
            pool.TakeAtLeast(simulation.Bodies.HandleToLocation.Length, out bodyData);
            pool.TakeAtLeast(simulation.Statics.HandleToIndex.Length, out staticData);
        }

        /// <summary>
        /// 获取对与正文句柄关联的属性的引用。
        /// </summary>
        /// <param name="bodyHandle">要检索其属性的正文句柄。</param>
        /// <return>对与正文句柄关联的属性的引用。</return>
        public ref T this[BodyHandle bodyHandle]
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                Debug.Assert(simulation.Bodies.BodyExists(bodyHandle));
                return ref bodyData[bodyHandle.Value];
            }
        }
        /// <summary>
        /// 获取对与静电句柄关联的属性的引用。
        /// </summary>
        /// <param name="staticHandle">要检索其属性的静电句柄。</param>
        /// <return>对与静电句柄关联的属性的引用。</return>
        public ref T this[StaticHandle staticHandle]
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                Debug.Assert(simulation.Statics.StaticExists(staticHandle));
                return ref staticData[staticHandle.Value];
            }
        }
        /// <summary>
        /// 对象关联的属性的引用。
        /// </summary>
        /// <param name="staticHandle">检索属性的可碰撞。</param>
        /// <return>对与可冲突的属性关联的属性的引用。</return>
        public ref T this[CollidableReference collidable]
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                if (collidable.Mobility == CollidableMobility.Static)
                    return ref this[collidable.StaticHandle];
                return ref this[collidable.BodyHandle];
            }
        }

        /// <summary>
        /// 确保有空间容纳给定的正文句柄,并返回对已用内存的引用。
        /// </summary>
        /// <param name="bodyHandle">要分配的正文句柄。</param>
        /// <return>对给定正文数据的引用。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public ref T Allocate(BodyHandle bodyHandle)
        {
            Debug.Assert(simulation.Bodies.BodyExists(bodyHandle), "The body handle should have been allocated in the bodies set before any attempt to create a property for it.");
            if (bodyHandle.Value >= bodyData.Length)
            {
                var targetCount = BufferPool.GetCapacityForCount<T>(simulation.Bodies.HandlePool.HighestPossiblyClaimedId + 1);
                Debug.Assert(targetCount > bodyData.Length, "Given what just happened, we must require a resize.");
                pool.ResizeToAtLeast(ref bodyData, targetCount, Math.Min(simulation.Bodies.HandlePool.HighestPossiblyClaimedId + 1, bodyData.Length));
            }
            return ref bodyData[bodyHandle.Value];
        }

        /// <summary>
        /// 确保给定静电句柄有空间,并返回对已用内存的引用。
        /// </summary>
        /// <param name="Handle">要分配的静电句柄。</param>
        /// <return>对给定静电的数据的引用。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public ref T Allocate(StaticHandle handle)
        {
            Debug.Assert(simulation.Statics.StaticExists(handle), "The static handle should have been allocated in the statics set before any attempt to create a property for it.");
            if (handle.Value >= staticData.Length)
            {
                var targetCount = BufferPool.GetCapacityForCount<T>(simulation.Statics.HandlePool.HighestPossiblyClaimedId + 1);
                Debug.Assert(targetCount > staticData.Length, "Given what just happened, we must require a resize.");
                pool.ResizeToAtLeast(ref staticData, targetCount, Math.Min(simulation.Statics.HandlePool.HighestPossiblyClaimedId + 1, staticData.Length));
            }
            return ref staticData[handle.Value];
        }

        /// <summary>
        /// 确保有空间容纳给定的可冲突引用,并返回对已用内存的引用。
        /// </summary>
        /// <param name="Handle">要分配的可冲突引用。</param>
        /// <return>对给定可冲突对象数据的引用。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public ref T Allocate(CollidableReference collidableReference)
        {
            if (collidableReference.Mobility == CollidableMobility.Static)
            {
                return ref Allocate(collidableReference.StaticHandle);
            }
            return ref Allocate(collidableReference.BodyHandle);
        }

        /// <summary>
        /// 确保内部结构至少具有指定的机构容量。
        /// </summary>
        /// <param name="Capacity">要确保的容量。</param>
        public void EnsureBodyCapacity(int capacity)
        {
            var targetCount = BufferPool.GetCapacityForCount<T>(Math.Max(simulation.Bodies.HandlePool.HighestPossiblyClaimedId + 1, capacity));
            if (targetCount > bodyData.Length)
            {
                pool.ResizeToAtLeast(ref bodyData, targetCount, Math.Min(bodyData.Length, simulation.Bodies.HandlePool.HighestPossiblyClaimedId + 1));
            }
        }
        /// <summary>
        /// 确保内部结构至少具有给定的静力学能力。
        /// </summary>
        /// <param name="Capacity">要确保的容量。</param>
        public void EnsureStaticCapacity(int capacity)
        {
            var targetCount = BufferPool.GetCapacityForCount<T>(Math.Max(simulation.Statics.HandlePool.HighestPossiblyClaimedId + 1, capacity));
            if (targetCount > staticData.Length)
            {
                pool.ResizeToAtLeast(ref staticData, targetCount, Math.Min(staticData.Length, simulation.Statics.HandlePool.HighestPossiblyClaimedId + 1));
            }
        }

        /// <summary>
        /// 根据Body集合将Body集合使用的内存压缩到安全的最小值。
        /// </summary>
        public void CompactBodies()
        {
            var targetCount = BufferPool.GetCapacityForCount<T>(simulation.Bodies.HandlePool.HighestPossiblyClaimedId + 1);
            if (targetCount < bodyData.Length)
            {
                pool.ResizeToAtLeast(ref bodyData, targetCount, simulation.Bodies.HandlePool.HighestPossiblyClaimedId + 1);
            }
        }
        /// <summary>
        /// 根据Statics集合将集合用于Statics的内存压缩到安全的最小值。
        /// </summary>
        public void CompactStatics()
        {
            var targetCount = BufferPool.GetCapacityForCount<T>(simulation.Statics.HandlePool.HighestPossiblyClaimedId + 1);
            if (targetCount < staticData.Length)
            {
                pool.ResizeToAtLeast(ref staticData, targetCount, simulation.Statics.HandlePool.HighestPossiblyClaimedId + 1);
            }
        }

        /// <summary>
        /// 返回所有持有的资源。
        /// </summary>
        public void Dispose()
        {
            pool.Return(ref bodyData);
            pool.Return(ref staticData);
        }
    }
}
